package net.sf.nwn.jme3;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.jme3.asset.AssetManager;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.BatchNode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;

public class ControllableModel {

	private final ControllableModelPrototype prototype;
	private BatchNode mainNode;
	private AnimationBehavior animationBehavior;

	public ControllableModel(ControllableModelPrototype prototype, AssetManager manager, ShadingType metallic) {
		this.prototype = prototype;
		GeomNode modelGeometry = prototype.getBaseModel().getModelGeometry();
		Node geometryNode = modelGeometry.createAllTG(manager, metallic);
                MaterialManager.getInstance().destroy();
		geometryNode.setShadowMode(ShadowMode.Cast);
		geometryNode.rotate(((float)-Math.PI/2), 0, 0);
		geometryNode.rotate(0,0,((float)Math.PI));
		this.mainNode = new BatchNode(geometryNode.getName());
                		
                this.mainNode.attachChild(geometryNode);
		final Map<String, Node> namedParts = findNamedParts(mainNode);
		final Map<String,EmitterBehavior> emitters =  findEmitterBehaviors(mainNode);
		Collection<Geometry> skinNodes = findSkinNodes(mainNode);
		this.animationBehavior = new AnimationBehavior(namedParts,emitters,prototype.getAnimations(),prototype.getDefAnims(), skinNodes);
		this.mainNode.addControl(animationBehavior);
                
                mainNode.batch();
	}

	public Node getMainNode() {
		return mainNode;
	}
	
	public void playAnimation(String name, boolean wantLoop) {
		animationBehavior.playAnimation(name, wantLoop);
	}
	
	public void playDefaultAnimation() {
		animationBehavior.playDefaultAnimation();
	}
	
	
	public void disableDefaultAnimation() {
		animationBehavior.disableDefaultAnimation();
	}
	
	public void setDefaultAnimations(List<String> anims) {
		animationBehavior.setDefaultAnimations(anims);
	}
	
	public void setAnimationTimeScale(float animationTimeScale) {
		animationBehavior.setAnimationTimeScale(animationTimeScale);
	}
	
	public Set<String> getAllAnimationNames() {
		return prototype.getAnimations().keySet();
	}
	
	public String getName() {
		return prototype.getName();
	}
	
	private static Map<String, EmitterBehavior> findEmitterBehaviors(Node node) {
		Map<String, EmitterBehavior> map = new HashMap<String, EmitterBehavior>();
		findEmitterBehaviors(node, map);
		return map;
	}

	private static void findEmitterBehaviors(Node node, Map<String, EmitterBehavior> map) {
		EmitterBehavior control = node.getControl(EmitterBehavior.class);
		
		if ( control != null ) {
			map.put(control.getName(),control);
		}

		for (Spatial sp : node.getChildren()) {
			if (sp instanceof Node) {
				findEmitterBehaviors((Node) sp, map);
			}
		}
	}
	

	private static Map<String, Node> findNamedParts(Node node) {
		Map<String, Node> map = new HashMap<String, Node>();
		findNamedParts(node, map);
		return map;
	}

	private static void findNamedParts(Node node, Map<String, Node> map) {

		map.put(node.getName(), node);

		for (Spatial sp : node.getChildren()) {
			if (sp instanceof Node) {
				findNamedParts((Node) sp, map);
			}
		}
	}
	
	
	private static Collection<Geometry> findSkinNodes(Node node) {
		Collection<Geometry> map = new ArrayList<Geometry>();
		findSkinNodes(node,map);
		return map;
	}

	private static void findSkinNodes(Node node, Collection<Geometry> map) {
		
		for (Spatial sp : node.getChildren()) {
			if (sp instanceof Node) {
				findSkinNodes((Node) sp, map);
			} else if ( sp instanceof Geometry ) {
				if ( sp.getUserData(SkinmeshGeomNode.SKIN_INFO) != null ) {
					map.add((Geometry)sp);
				}
			}
		}
	}

	
}
